<?php

namespace App\Services\Order;

use App\Jobs\AgentsAccumulateAfterPlacingOrdersJob;
use App\Models\Agent;
use App\Models\AgentRevenue;
use App\Models\AgentRewardRuleSettings;
use App\Models\Client;
use App\Models\CommodityActivity;
use App\Models\CommodityActivityPurchaseRestriction;
use App\Models\CommodityActivitySku;
use App\Models\Order;
use App\Services\Commodity;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpKernel\Exception\HttpException;

class PlaceOrder extends OperationBase
{
    private $skuWithNum;

    private $agent;

    private $client;

    private $remark;

    public function __construct(Agent $agent, Client $client, array $skuWithNum, string $remark = '')
    {
        $this->agent = $agent;
        $this->client = $client;
        $this->skuWithNum = $skuWithNum;
        $this->remark = $remark;
    }

    /**
     * @param Order|null $orderProp 下单没有 $orderProp
     * 2020-12-01 22:27:21 有些特殊参数从这里传进来了,比如 1.强扣(在重新下单时,代理商没钱了下单扣成负数) 2.SDK 下单的自定义订单号和发货回调地址
     * @throws \Exception
     */
    public function handle($orderProp = null)
    {
        // 获取 sku 数据
        $skuList = CommodityActivitySku::query()
            ->with([
                'attachments',
                'values.attribute',
                'commodityActivity' => function ($query) {
                    $query
                        ->with('commodity', 'attachments')
                        ->putOnShelf()
                        ->panicBuyingTime();
                }])
            ->whereIn('id', array_keys($this->skuWithNum))
            ->get();
        $skuListArr = arrayCombineByKey($skuList->toArray(), 'id');

        // 计算 商品数、价格和佣金
        $commodityTotalNum = 0; // 商品总数
        $commodityPrice = 0; // 商品总价格
        $totalIncome = 0; // 总收入
        $currentIdentity = $this->agent->currentLevelIdentity()['id']; // 当前用户身份
        foreach ($this->skuWithNum as $skuId => &$skuItem) {
            // 活动是否在抢购时间内 是否上架 (限制条件在 skuList with 里, 这里只判断空就行了)
            if (empty($sku = $skuListArr[$skuId]) || empty($sku['commodity_activity'])) {
                // 更新缓存, 判断最近 十五分钟内是否有更新过
                if (Redis::set('place_order_clean_cache:' . $skuListArr[$skuId]['commodity_activity_id'], Carbon::now()->timestamp, 'EX', 15 * 60, 'NX') == true) {
                    Commodity::forgetCommodityActivityListCacheTag();
                }

                abort(400, '非常抱歉,商品不存在或已下架!请刷新活动列表或联系我们');
            }
            ($skuItem['num'] > $sku['stock']) && abort(400, '库存不足'); // 这里前置判断下库存是否够, 严格的让 mysql 字段不能为负来

            $skuItem['unit_price'] = $sku[$currentIdentity . '_price']; // 单价
            $skuItem['sku_total_price'] = $skuItem['unit_price'] * $skuItem['num']; // 总价
            $commodityTotalNum += $skuItem['num']; // 商品总数
            $commodityPrice += $skuItem['sku_total_price']; // 商品总价

            $skuItem['income'] = $sku['external_guide_price'] - $skuItem['unit_price']; // 当前代理商收入
            $skuItem['income'] = ($skuItem['income'] < 0 ? 0 : $skuItem['income']) * $skuItem['num']; // 收入不能为负数
            $totalIncome += $skuItem['income']; // 总收入
        }

        $commodityActivity = array_values(arrayCombineByKey(Arr::pluck($skuListArr, 'commodity_activity'), 'id')); // 商品活动
        // 不止一个 商品活动 的 sku 被提交
        if (count($commodityActivity) > 1) {
            abort(400, '商品数据异常');
        }
        $commodityActivity = $commodityActivity[0];

        // 判断限购
        $addressPurchaseRestriction = $commodityActivity['address_purchase_restriction'] ?? 0;
        if ($addressPurchaseRestriction > 0) {
            $skuSum = array_sum(array_column($this->skuWithNum, 'num'));
            if ($skuSum > $addressPurchaseRestriction) {
                abort(400, sprintf('超出地址限购数量,限购数量%d,提交数量%d', $addressPurchaseRestriction, $skuSum));
            }
            $commodityActivityPurchaseRestriction = CommodityActivityPurchaseRestriction::query()->firstOrCreate([
                'commodity_activity_id' => $commodityActivity['id'],
                'province' => $this->client['province'] ?? '',
                'city' => $this->client['city'] ?? '',
                'area' => $this->client['area'] ?? '',
                'address' => $this->client['address'] ?? '',
            ], [
                'purchase_quantity' => $skuSum
            ]);
            // 不是本次创建
            if (!$commodityActivityPurchaseRestriction->wasRecentlyCreated) {
                // 之前的购买数 + 本次购买数 > 购买限制
                if (($commodityActivityPurchaseRestriction['purchase_quantity'] + $skuSum) > $addressPurchaseRestriction) {
                    abort(400, sprintf('超出地址限购数量,限购数量%d,已购数量%d', $addressPurchaseRestriction, $commodityActivityPurchaseRestriction['purchase_quantity']));
                }

                $commodityActivityPurchaseRestriction->increment('purchase_quantity', $skuSum);
            }
        }

        // 计算运费
        $freight = $this->calculationFreight($commodityActivity, $commodityTotalNum, $this->client->toArray());
        $orderAmount = $commodityPrice + $freight; // 订单价格
        if (
            ($orderAmount > ($this->agent['balance'] + $this->agent['unextractable_balance'])) // 余额不足
            && ! ($orderProp['force_deduction_balance'] ?? false) // 并且不允许强扣
        ) {
            abort(400, '余额不足');
        }

        // 计算 商品可提现余额 不可提现余额 分别支付金额
        $agentUnextractableBalance = $this->agent['unextractable_balance']; // 当前用户不可提前金额
        $agentBalance = $this->agent['balance']; // 当前用户可提前金额
        [$commodityBalancePaid, $commodityUnextractableBalancePaid] = Agent::deductionBalance($commodityPrice, $agentBalance, $agentUnextractableBalance, $orderProp['force_deduction_balance'] ?? false);
        [$freightBalancePaid, $freightUnextractableBalancePaid] = Agent::deductionBalance($freight, $agentBalance, $agentUnextractableBalance, $orderProp['force_deduction_balance'] ?? false);

        $order = Order::query()->create([
            'numbering' => $orderProp['numbering'] ?? $this->makeOrderNumbering(CommodityActivity::TYPE_WITH_ORDER_TYPE[$commodityActivity['type']]), // 允许自定义订单号 (SDK 方向)
            'remark' => $this->remark,
            'original_total_amount' => $orderAmount,
            'total_amount' => $orderAmount,
            'original_commodity_price' => $commodityPrice,
            'commodity_price' => $commodityPrice,
            'original_commodity_balance_paid' => $commodityBalancePaid,
            'commodity_balance_paid' => $commodityBalancePaid,
            'original_commodity_unextractable_balance_paid' => $commodityUnextractableBalancePaid,
            'commodity_unextractable_balance_paid' => $commodityUnextractableBalancePaid,
            'original_commodity_amount' => $commodityTotalNum,
            'commodity_amount' => $commodityTotalNum,
            'original_freight' => $freight,
            'freight' => $freight,
            'original_freight_balance_paid' => $freightBalancePaid,
            'freight_balance_paid' => $freightBalancePaid,
            'original_freight_unextractable_balance_paid' => $freightUnextractableBalancePaid,
            'freight_unextractable_balance_paid' => $freightUnextractableBalancePaid,
            'agent_id' => $this->agent->getKey(),
            'agent_snapshot' => array_merge($this->agent->toArray(), ['direct_inviter' => $this->agent->directInviter['id'] ?? null, 'indirect_inviter' => $this->agent->indirectInviter['id'] ?? null]), // 还原直接和间接邀请人数据
            'partner_id' => $this->agent->affiliatedPartner['id'] ?? 0,
            'original_income' => $totalIncome,
            'income' => $totalIncome,
            'direct_inviter' => $this->agent->directInviter['id'] ?? 0,
            'direct_inviter_snapshot' => $this->agent->directInviter ?? [],
            'indirect_inviter' => $this->agent->indirectInviter['id'] ?? 0,
            'indirect_inviter_snapshot' => $this->agent->indirectInviter ?? [],
            'commodity_id' => $commodityActivity['commodity']['id'],
            'commodity_name' => (isset($commodityActivity['name']) && !empty($commodityActivity['name'])) ? $commodityActivity['name'] : $commodityActivity['commodity']['name'], // 2021-01-10 20:45:59 增加活动名称,兼容没有名称的活动 以及 防止可能没有 name 字段的情况
            'commodity_numbering' => $commodityActivity['commodity']['numbering'],
            'commodity_snapshot' => $commodityActivity['commodity'],
            'commodity_activity_id' => $commodityActivity['id'],
            'commodity_activity_snapshot' => Arr::except($commodityActivity, 'commodity'),
            'client_id' => $this->client->getKey(),
            'client_name' => $this->client['name'],
            'client_phone' => $this->client['phone'],
            'client_address' => array_reduce([
                    $this->client->getRelationValue('province') ?? '',
                    $this->client->getRelationValue('city') ?? '',
                    $this->client->getRelationValue('area') ?? ''
                ], fn($address, $current) => $address . ($current['name'] ?? '')) . $this->client['address'], // 客户收货地址
            'client_snapshot' => $this->client,
            'supplier_id' => $commodityActivity['commodity']['supplier_id'],
            // 每日和每月的下单奖励设置快照
            'reward_setting_snapshot' => array_merge(
                \App\Services\AgentRewardRuleSettings::getSettingsByCache(AgentRewardRuleSettings::TYPE_DAILY_ORDER_REWARD),
                \App\Services\AgentRewardRuleSettings::getSettingsByCache(AgentRewardRuleSettings::TYPE_MONTHLY_ORDER_REWARD),
            ),
            'order_date' => Carbon::today(),
            'delivery_callback' => $orderProp['delivery_callback'] ?? '', // 回调地址 (SDK 方向)
        ]);
        // 订单 SKU
        $order->skus()->createMany($skuList->map(function ($sku) {
            $skuArr = $sku->toArray();
            Arr::forget($skuArr, 'commodity_activity');
            return [
                'commodity_id' => $sku['commodity_id'],
                'commodity_activity_id' => $sku['commodity_activity_id'],
                'commodity_activity_sku_id' => $sku['id'],
                'amount' => $this->skuWithNum[$sku['id']]['num'],
                'unit_price' => $this->skuWithNum[$sku['id']]['unit_price'],
                'income' => $this->skuWithNum[$sku['id']]['income'],
                'sku_snapshot' => $skuArr
            ];
        }));

        // 减库存
        $skuList->each(function (CommodityActivitySku $sku) {
            $sku->decrementStockAndIncrementSales($this->skuWithNum[$sku['id']]['num']);
        });

        // 用户余额支付 分可提和不可提的扣款 先扣不可提
        if ($this->agent['unextractable_balance'] < $orderAmount) {
            $this->agent->incrementOrDecrementBalance($orderAmount - ($this->agent['unextractable_balance'] < 0 ? 0 : $this->agent['unextractable_balance']), AgentRevenue::TYPE_PLACE_ORDER, $order, 'decrement');
            $this->agent->incrementOrDecrementUnextractableBalance($this->agent['unextractable_balance'], AgentRevenue::TYPE_PLACE_ORDER_UNEXTRACTABLE, $order, 'decrement');
        } else {
            // 正好扣完 和 还有剩余
            $this->agent->incrementOrDecrementUnextractableBalance($orderAmount, AgentRevenue::TYPE_PLACE_ORDER_UNEXTRACTABLE, $order, 'decrement');
        }

        // 下单各种数据 统计
        dispatch((new AgentsAccumulateAfterPlacingOrdersJob($order['id']))->delay(Carbon::now()->addSeconds(10)));

        return $order;
    }
}
